package cn.uncode.rpc.config;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.lang3.StringUtils;

import cn.uncode.rpc.common.log.Logger;
import cn.uncode.rpc.common.log.LoggerFactory;
import cn.uncode.rpc.config.annotation.ConfigDesc;
import cn.uncode.rpc.exception.FrameworkException;

public class Config {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(Config.class);
	
	protected final Properties properties;
	
	public Config(){
		this.properties = new Properties();
	}
	
	
	public Config(Properties props){
		this.properties = props;
	}
	
	public Config(String filename){
		properties = new Properties();
		File cfg = new File("./"+filename);
        FileInputStream fis = null;
        try {
        	
    		if (cfg.exists()) {
    			fis = new FileInputStream(cfg);
                properties.load(fis);
            }else{
            	throw new RuntimeException("Config file is null");
            }
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        } finally {
            closeQuietly(fis);
        }
	}
	
	public Config(File cfg){
		properties = new Properties();
        FileInputStream fis = null;
        try {
    		if (cfg.exists()) {
    			fis = new FileInputStream(cfg);
                properties.load(fis);
            }else{
            	throw new RuntimeException("Config file is null");
            }
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        } finally {
            closeQuietly(fis);
        }
	}
	
	
	
	/**
     * 按顺序进行config 参数append and override，按照configs出现的顺序，后面的会覆盖前面的相同名称的参数
     * 
     */
    protected void collectConfigParams(Config... configs) {
        for (Config config : configs) {
            if (config != null) {
            	//
            	//
            	//
            }
        }
    }
    
    protected void appendConfigParams(String prefix, Config... configs) {
    	if (configs != null) {
    		for (Config config : configs) {
                if (config != null) {
                	if(StringUtils.isNotBlank(prefix)){
                		config.appendConfigParams(prefix);
                	}else{
                		config.appendConfigParams();
                	}
                }
            }
    	}
    }

    /**
     * 将config参数录入properties中
     * 
     */
    protected void appendConfigParams() {
    	appendConfigParams("");
    }
	
	/**
     * 将config参数录入properties中
     * 
     */
    protected void appendConfigParams(String prefix) {
        Method[] methods = this.getClass().getMethods();
        for (Method method : methods) {
            try {
                String name = method.getName();
                if (isConfigMethod(method)) {
                    int idx = name.startsWith("get") ? 3 : 2;
                    String prop = name.substring(idx, idx + 1).toLowerCase() + name.substring(idx + 1);
                    String key = prop;
                    ConfigDesc configDesc = method.getAnnotation(ConfigDesc.class);
                    if (configDesc != null && !StringUtils.isBlank(configDesc.key())) {
                        key = configDesc.key();
                    }

                    Object value = method.invoke(this);
                    if (value == null || StringUtils.isBlank(String.valueOf(value))) {
                        if (configDesc != null && configDesc.required()) {
                            throw new FrameworkException(String.format("%s.%s should not be null or empty", this.getClass()
                                    .getSimpleName(), key));
                        }
                        continue;
                    }
                    if (prefix != null && prefix.length() > 0) {
                        key = prefix + "." + key;
                    }
                    properties.put(key, String.valueOf(value).trim());
                } else if ("getParameters".equals(name) && Modifier.isPublic(method.getModifiers())
                        && method.getParameterTypes().length == 0 && method.getReturnType() == Map.class) {
                    @SuppressWarnings("unchecked")
					Map<String, String> map = (Map<String, String>) method.invoke(this);
                    if (map != null && map.size() > 0) {
                        String pre = prefix != null && prefix.length() > 0 ? prefix + "." : "";
                        for (Map.Entry<String, String> entry : map.entrySet()) {
                        	properties.put(pre + entry.getKey(), entry.getValue());
                        }
                    }
                }
            } catch (Exception e) {
                throw new FrameworkException(String.format("Error when append params for config: %s.%s", this.getClass()
                        .getSimpleName(), method.getName()), e);
            }
        }
    }
    
    
    private boolean isConfigMethod(Method method) {
        boolean checkMethod =
                (method.getName().startsWith("get") || method.getName().startsWith("is")) && !"isDefault".equals(method.getName())
                        && Modifier.isPublic(method.getModifiers()) && method.getParameterTypes().length == 0
                        && isPrimitive(method.getReturnType());

        if (checkMethod) {
            ConfigDesc configDesc = method.getAnnotation(ConfigDesc.class);
            if (configDesc != null && configDesc.excluded()) {
                return false;
            }
        }
        return checkMethod;
    }
    

    private boolean isPrimitive(Class<?> type) {
        return type.isPrimitive() || type == String.class || type == Character.class || type == Boolean.class || type == Byte.class
                || type == Short.class || type == Integer.class || type == Long.class || type == Float.class || type == Double.class;
    }
	
	
    public String getString(String name, String defaultValue) {
        return properties.containsKey(name) ? properties.getProperty(name) : defaultValue;
    }

    public String getString(String name) {
        if (properties.containsKey(name)) {
            return properties.getProperty(name);
        }
        throw new IllegalArgumentException("Missing required property '" + name + "'");
    }

    public int getInt(String name) {
        if (properties.containsKey(name)) {
            return getInt(name, -1);
        }
        throw new IllegalArgumentException("Missing required property '" + name + "'");
    }

    public int getInt(String name, int defaultValue) {
    	 int v = defaultValue;
         if (properties.containsKey(name)) {
             v = Integer.valueOf(properties.getProperty(name));
         }
         return v;
    }

    public boolean getBoolean(String name, boolean defaultValue) {
        if (!properties.containsKey(name)) return defaultValue;
        return "true".equalsIgnoreCase(properties.getProperty(name));
    }
    
    public Properties getProperties() {
		return properties;
	}
    



	private void closeQuietly(java.io.Closeable closeable) {
        if (closeable == null) return;
        try {
            closeable.close();
        } catch (IOException e) {
        	LOGGER.error(e.getMessage(), e);
        }
    }
    
    
    
    
}
